package cn.darkhorse.device.iot;

import cn.darkhorse.device.dao.IotDataDao;
import cn.darkhorse.device.iot.constant.IotConstant;
import cn.darkhorse.device.pojo.IotPointTable;
import cn.darkhorse.device.pojo.MqttData;
import cn.darkhorse.device.service.IotPointTableService;
import cn.darkhorse.device.service.MqttDataService;
import cn.hutool.db.sql.SqlBuilder;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class IotDataAnalyzer {
    private static final Logger log = LoggerFactory.getLogger(IotDataAnalyzer.class);
    @Autowired
    private ApplicationContext applicationContext;
    @Resource
    private MqttDataService mqttDataService;
    @Resource
    private IotDataDao iotDataDao;
    @Resource
    private IotPointTableService pointTableService;
    @Resource
    private RedisCache redisCache;


    private static Map<String, String> parsers = new HashMap<>();
    private static Map<String, String> sqlBuilders = new HashMap<>();

    static {
        // 后期可以改为数据库或配置文件中
        parsers.put("0101", "sparksIotParser");
        sqlBuilders.put("0101", "defaultSqlBuilder");
        parsers.put("0201", "geIoParser");
        sqlBuilders.put("0201", "defaultSqlBuilder");
        parsers.put("0301", "TasTekParser");
        sqlBuilders.put("0301", "defaultSqlBuilder");
    }

    /**
     * 数据分析
     * @param data
     */
    public void analysis(MqttData data) {
        try {
            // Step.1 解析数据
            String parserName = parsers.get(data.getDataType());
            IParser parser = applicationContext.getBean(parserName, IParser.class);

            if (parser != null) {
                String gatewayDeviceId = parser.getGatewayDeviceId(data);
                Set<String> historyTables = getHistoryTables();
                String historyTableName = IotConstant.HISTORY_TABLE_NAME_PRE + gatewayDeviceId;

                if (!historyTables.contains(historyTableName.toLowerCase())) {
                    log.warn("未找到表：" + historyTableName);
//                    updateMqttDataFlag(data.getId(), IotConstant.DATA_FLAG_NO_HISTORY_TABLE);
                    return;
                }

                LocalDateTime createdAd = parser.getCreatedAd(data);
                Map<String, Object> params = parser.parse(data);
                // 避免使用双层循环 先组成map
                List<IotPointTable> tableList = pointTableService.lambdaQuery()
                        .eq(IotPointTable::getGatewayDeviceId, gatewayDeviceId)
                        .list();
                Map<String, IotPointTable> pointMap = tableList.stream()
                        .collect(Collectors.toMap(IotPointTable::getFieldName, Function.identity()));

                params.keySet().forEach(fieldName -> {
                    IotPointTable pointTable = pointMap.get(fieldName);
                    params.put(fieldName, pointTableService.buildValue(pointTable, params.get(fieldName)));
                });

                redisCache.setCacheMap(IotConstant.CACHE_MQTT_DATA_REAL_DATA + gatewayDeviceId, params);
                // Step.2 构造sql语句
                String sqlBuilderName = sqlBuilders.get(data.getDataType());
                ISqlBuilder sqlBuilder = applicationContext.getBean(sqlBuilderName, ISqlBuilder.class);


                String sql = sqlBuilder.build(params, gatewayDeviceId, createdAd);

                log.debug("analysis 构造sql：" + sql);
                // Step.3 执行
                SqlRunner.db().insert(sql);
            }

//            updateMqttDataFlag(data.getId(), IotConstant.DATA_FLAG_SUCCESS);
        }catch (Exception e) {
//            updateMqttDataFlag(data.getId(), IotConstant.DATA_FLAG_ERROR);
            e.printStackTrace();
        }
    }
//    弃用  实测500条数据耗时155秒,效率过低,一次循环要查询3次数据库,且重复查询太多
    public String getSql(MqttData data) {
        try {
            // Step.1 解析数据
            String parserName = parsers.get(data.getDataType());
            IParser parser = applicationContext.getBean(parserName, IParser.class);

            if (parser != null) {
                String gatewayDeviceId = parser.getGatewayDeviceId(data);
                Set<String> historyTables = getHistoryTables();
                String historyTableName = IotConstant.HISTORY_TABLE_NAME_PRE + gatewayDeviceId;

                if (!historyTables.contains(historyTableName.toLowerCase())) {
                    log.warn("未找到表：" + historyTableName);
                    return null;
                }

                LocalDateTime createdAd = parser.getCreatedAd(data);
                Map<String, Object> params = parser.parse(data);
                // 避免使用双层循环 先组成map
                List<IotPointTable> tableList = pointTableService.lambdaQuery()
                        .eq(IotPointTable::getGatewayDeviceId, gatewayDeviceId)
                        .list();
                Map<String, IotPointTable> pointMap = tableList.stream()
                        .collect(Collectors.toMap(IotPointTable::getFieldName, Function.identity()));

                params.keySet().forEach(fieldName -> {
                    IotPointTable pointTable = pointMap.get(fieldName);
                    params.put(fieldName, pointTableService.buildValue(pointTable, params.get(fieldName)));
                });

                redisCache.setCacheMap(IotConstant.CACHE_MQTT_DATA_REAL_DATA + gatewayDeviceId, params);
                // Step.2 构造sql语句
                String sqlBuilderName = sqlBuilders.get(data.getDataType());
                ISqlBuilder sqlBuilder = applicationContext.getBean(sqlBuilderName, ISqlBuilder.class);
                String sql = sqlBuilder.build(params, gatewayDeviceId, createdAd);

                log.debug("analysis 构造sql：" + sql);
                // Step.3 执行
                return sql;
            }
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }


//    性能优化  500条数据1秒完成处理 ,速度提升N倍
    public List<String> getSql(List<MqttData> dataList) {

        List<String> sqlList = new ArrayList<>();
        Set<String> historyTables = getHistoryTables();
        // 获取所有ID并去重
        Set<String> deviceIds = dataList.stream().map(MqttData::getDeviceId).collect(Collectors.toSet());
        if (deviceIds.isEmpty()){
            return sqlList;
        }

        // 批量获取IotPointTable
        List<IotPointTable> iotPointTables = pointTableService.lambdaQuery()
                .in(IotPointTable::getGatewayDeviceId, deviceIds)
                .list();
        Map<String, List<IotPointTable>> deviceToPointTables = iotPointTables.stream().collect(Collectors.groupingBy(IotPointTable::getGatewayDeviceId));
        Set<String> keySet = deviceToPointTables.keySet();
        // 预处理 这里使用一次循环进行预处理, 形成一个map ,目的是优化速度 ,避免多次循环或多次查询数据库
        Map<String, Map<String, IotPointTable>> devicePointMap = keySet.stream()
                .collect(Collectors.toMap(Function.identity(),
                        deviceId -> deviceToPointTables.get(deviceId).stream()
                                .collect(Collectors.toMap(IotPointTable::getPointId, Function.identity()))
                ));
        // 避免多次实例化
        Map<String, ISqlBuilder> sqlBuilderMap = new HashMap<>();
        Map<String, IParser> parserMap = new HashMap<>();
        dataList.stream()
                .map(MqttData::getDataType)
                .distinct()  //获取不重复的dataType集合
                .forEach(dataType -> {
                    String sqlBuilderName = sqlBuilders.get(dataType);
                    ISqlBuilder sqlBuilder = applicationContext.getBean(sqlBuilderName, ISqlBuilder.class);
                    sqlBuilderMap.put(dataType, sqlBuilder);

                    String parserName = parsers.get(dataType);
                    IParser parser = applicationContext.getBean(parserName, IParser.class);
                    parserMap.put(dataType, parser);
                });

        dataList.forEach(data-> {
            System.out.println(data);
            System.out.println(data.getData());
                        IParser parser = parserMap.get(data.getDataType());
                        // Step.1 解析数据
                        if (parser != null) {

                            String gatewayDeviceId = parser.getGatewayDeviceId(data);

                            String historyTableName = IotConstant.HISTORY_TABLE_NAME_PRE + gatewayDeviceId;


                            if (!historyTables.contains(historyTableName.toLowerCase())) {
                                log.warn("未找到表：" + historyTableName);
                                return;
                            }

                            LocalDateTime createdAd = parser.getCreatedAd(data);


                            Map<String, IotPointTable> pointMap = devicePointMap.get(gatewayDeviceId);
                            if (pointMap == null || pointMap.isEmpty()) {
                                log.warn("未找到pointMap：" + gatewayDeviceId);
                                return;
                            }
                            System.out.println("gatewayDeviceId:"+gatewayDeviceId);
                            System.out.println("devicePointMap:"+devicePointMap);
                            System.out.println("pointMap:"+pointMap);
                            System.out.println(pointMap.keySet());
                            System.out.println("data:"+data.getData());
                            Map<String, Object> params = parser.parse(data,pointMap.keySet());
                            //预处理, 省略DefaultSqlBuilder/getPointFieldLookup 方法中的数据库查询步骤,进一步优化速度
                            Map<String, String> pointIdToFiledNameMap = new HashMap<>();

                            params.keySet().forEach(pointId -> {
                                System.out.println("point:"+pointId);
                                System.out.println("param:"+params.get(pointId));
                                IotPointTable pointTable = pointMap.get(pointId);
                                params.put(pointId, pointTableService.buildValue(pointTable, params.get(pointId)));

                                pointIdToFiledNameMap.put(pointTable.getPointId(), pointTable.getFieldName());
                            });

                            redisCache.setCacheMap(IotConstant.CACHE_MQTT_DATA_REAL_DATA + gatewayDeviceId, params);
                            // Step.2 构造sql语句
                            ISqlBuilder sqlBuilder = sqlBuilderMap.get(data.getDataType());
                            String sql = sqlBuilder.build(params, gatewayDeviceId, createdAd, pointIdToFiledNameMap);

                            log.debug("getSql 构造sql：" + sql);
                            // Step.3 执行
                            if (StringUtils.isNotEmpty(sql)) {
                                sqlList.add(sql);
                            };
                        }

                }

        );
        return sqlList;
    }
    /**
     * 更新状态
     * @param id
     * @param flag
     */
//    private void updateMqttDataFlag(Integer id, int flag) {
//        MqttData mqttData = new MqttData();
//        mqttData.setId(id);
//        mqttData.setFlag(flag);
//        mqttDataService.updateById(mqttData);
//    }

    /**
     * 查询所有历史表（以history_打头的表）的表名
     * @return
     */
    private Set<String> getHistoryTables() {
        List<Map<String, Object>> maps = iotDataDao.getHistoryTables();
        Set<String> tableNames = new HashSet<>();
        for (Map<String, Object> map : maps) {
            String tableName = ((String) map.get("tableName")).toLowerCase();
            tableNames.add(tableName);
        }

        return tableNames;
    }
}
